package com.ib.algokit.ib;
import org.activequant.core.domainmodel.InstrumentSpecification;
import org.activequant.core.domainmodel.account.Order;
import org.activequant.core.types.Currency;
import org.activequant.core.types.OrderType;
import org.activequant.core.types.SecurityType;

import com.ib.algokit.forex.CurrencyPairs;

public class CommissionsCalculator {
	/**
	 * returns how much a one way trip with IB would cost for the given order(Order) to fill at once
	 * @param price
	 * @return
	 */
//	public static double determineApproximateCommissionForOrder(Order order){
//		return determineApproximateCommissionForOrderPartialFill(order, -1, 0);
//	}
	/**
	 * returns how much a one way trip with IB would cost for the given order(Order) to fill partially (the given partialFillQuantity(double))
	 * @param price
	 * @return
	 */
	public static double determineApproximateCommissionForOrderPartialFill(Order order, double partialFillQuantity, double currentMarketPrice){
		InstrumentSpecification spec=order.getInstrumentSpecification();
		SecurityType securityType=spec.getSecurityType();
		double quantity=partialFillQuantity>=0?partialFillQuantity:order.getQuantity();
		double price=0;
		double commission=0;//USD values for now
		OrderType orderType=order.getOrderType();
//		OrderSide orderSide=order.getOrderSide();
		String exchange=spec.getExchange();
		
		switch(orderType) {
			case MARKET: 
				price=currentMarketPrice;
				break;
			case LIMIT: 
				price=order.getLimitPrice();				
				break;
			case STOP: 
				price=order.getStopPrice();
				break;
			case STOP_LIMIT:
	
				break;
			case TRAILING_STOP:
				
				break;
			case MARKET_TO_LIMIT: 
				price=currentMarketPrice;
				break;	
			default:
				throw new IllegalArgumentException("order type: " + orderType + " is not supported by this broker");
		}
		
		switch(securityType){
			case STOCK:
				if("SMART".equals(exchange)){
					double tradeValue= price*quantity;
					double minCommissionPerOrder=1; //min 1 USD per Order
					commission=0.005*quantity;//USD value ($0.005 * share)
					double maxCommission=tradeValue*0.005;//USD value (0.5% of trade value)
					commission=minOf(commission, maxCommission);
					commission=maxOf(commission, minCommissionPerOrder);
				}else{
					double tradeValue= price*quantity;
					double minCommissionPerOrder=1.3; //min 1.3 USD per Order
					if(quantity<=500){
						commission=0.013*quantity;//USD value ($0.013 * share)
					}else{
						commission=0.008*quantity;//USD value ($0.008 * share)
					}
					double maxCommission=tradeValue*0.005;//USD value (0.5% of trade value)
					commission=minOf(commission, maxCommission);
					commission=maxOf(commission, minCommissionPerOrder);
				}		
				break;
			case CASH:
					Currency currency=spec.getCurrency();
					double tradeValue= price*quantity;
					//double tradeValueUSD = currency==Currency.USD?tradeValue:CurrencyPairs.convert(currency, tradeValue, Currency.USD);
					double minCommissionPerOrder=(currency==Currency.USD)?2.5:CurrencyPairs.convert(Currency.USD, 2.5, currency);//min 2.5 USD per Order
					//commission=tradeValueUSD*(0.2*CurrencyPairs.PIP);//USD value (0.2 PIP)
					commission=tradeValue*(0.2*CurrencyPairs.PIP);
					commission=maxOf(commission, minCommissionPerOrder);
				break;	
		}
		return commission;
	}
	/**
	 * determines optimal quantity for the given order(Order), so minimum commissions (at IB) apply and i get max quantity
	 * @param order
	 * @return
	 */
	public static double determineOrderQuantityForOptimalMinimumCommission(Order order){
		InstrumentSpecification spec=order.getInstrumentSpecification();
		SecurityType securityType=spec.getSecurityType();
		double quantity=0;
//		double price=0;
		OrderType orderType=order.getOrderType();
//		OrderSide orderSide=order.getOrderSide();
		String exchange=spec.getExchange();
		
//		switch(orderType) {
//			case MARKET: 
//				//price=order.getQuantity();
//				break;
//			case LIMIT: 
////				price=order.getLimitPrice();				
//				break;
//			case STOP: 
////				price=order.getStopPrice();
//				break;
//			case STOP_LIMIT:
//				break;
//			case TRAILING_STOP:				
//				break;
//			case MARKET_TO_LIMIT: 
//				//price=order.getQuantity();
//				break;		
//			default:
//				throw new IllegalArgumentException("order type: " + orderType + " is not supported by this broker");
//		}
		
		switch(securityType){
			case STOCK:
				if("SMART".equals(exchange)){
					double minCommissionPerOrder=1; //min 1 USD per Order
					
					quantity=minCommissionPerOrder/0.005;
//					double maxCommission=tradeValue*0.005;//USD value (0.5% of trade value)
//					commission=minOf(commission, maxCommission);
//					commission=maxOf(commission, minCommissionPerOrder);
				}else{
					double minCommissionPerOrder=1.3; //min 1.3 USD per Order
					quantity=minCommissionPerOrder/0.013;
					if(quantity>500){
						quantity=minCommissionPerOrder/0.008;//USD value ($0.008 * share)
					}
				}		
				break;
			case CASH:
				if("SMART".equals(exchange)){
					double minCommissionPerOrder=2.5; //min 2.5 USD per Order
					
					quantity=minCommissionPerOrder/(0.2*CurrencyPairs.PIP);//quantity is in USD
//					double maxCommission=tradeValue*0.005;//USD value (0.5% of trade value)
//					commission=minOf(commission, maxCommission);
//					commission=maxOf(commission, minCommissionPerOrder);
				}		
				break;	
		}
		return quantity;
	}
	
    /**
     * returns the minimum value of two doubles.<br/>
     * returns the minimum from the given v1(double) and v2(double)
     * @param v1
     * @param v2
     * @return minimum of a value of two doubles.
     */
    public static double minOf(double v1, double v2) {
        if (v1 <= v2)
            return v1;
        if (v2 <= v1)
            return v2;
        return v1;
    }
    /**
     * returns the maximum from the given v1(double) and v2(double)
     * @param v1
     * @param v2
     * @return
     */
    public static double maxOf(double v1, double v2) {
        if (v1 >= v2 )
            return v1;
        else return v2;
    }
}
